﻿#include "StdAfx.h"
#include "eng_type.h"
#include "Dbc.h"
#include "Stmt.h"
#include "Globalvf.h"
#include "Lite_Util.h"
#include "sqlite3.h"
#include "LiteODBC.h"

#define WARR()    WriteArray("d:\\temp\\Arr.txt",__FUNCTION__,__LINE__);
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 클래스 생성 및 삭제.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 생성자.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
CSTMT::CSTMT(const int nMyIndex, DBC *pDbc)
{
	//==================================================
	// 초기화 설정
	//==================================================
	m_nMyIndex = nMyIndex;
	m_pDbc = pDbc;
    //==================================================
	// 작동을 위한 변수들 초기화.
	//==================================================
	//struct stmt *next;	/**< Linkage for STMT list in DBC */
    memset(cursorname,0,32);	/**< Cursor name */
	memset(m_szSQL,0,4096);		/**< Current query, raw string */
	query = NULL;
    oemcp = &m_pDbc->oemcp;
    isselect = 0;		/**< > 0 if query is a SELECT statement */
    m_pCols = NULL;			/**< Result column array */
    m_pDynCols = NULL;		/**< Column array, but malloc()ed */
    m_nDcols = 0;			/**< Number of entries in m_pDynCols */
    bkmrk = 0;			/**< True when bookmarks used */
    memset(&bkmrkcol,0,sizeof(BINDCOL));		/**< Bookmark bound column */
    m_pBindCols = NULL;		/**< Array of bound columns */
    m_nBindColCount = 0;		/**< Number of entries in bindcols */
    nbindparms = 0;		/**< Number bound parameters */
    bindparms =  NULL;	/**< Array of bound parameters */
    nparams =  0;		/**< Number of parameters in query */
    m_nRowCount = 0;			/**< Number of result rows */
    rowp = 0;			/**< Current result row */
    rows = NULL;		/**< 2-dim array, result set */
    nrowfreetype = 0;      /** row free type **/
    naterr = 0;			/**< Native error code */
    memset(sqlstate,0,6);		/**< SQL state for SQLError() */
    memset(logmsg,0,1024);	/**< Message for SQLError() */
    nowchar[0] = m_pDbc->nowchar;
    nowchar[1] = 0;
	dobigint = m_pDbc->dobigint;
    longnames = m_pDbc->longnames;		/**< Don't shorten column names */
    retr_data = SQL_RD_ON;		/**< SQL_ATTR_RETRIEVE_DATA */
    rowset_size = 1;	/**< Size of rowset */
    row_status = NULL;	/**< Row status pointer */
    row_status0 = &row_status1;
    row_status1 = 0;	/**< Internal status array for 1 row rowsets */
	row_count = NULL;	/**< Row count pointer */
    row_count0 = 0;	/**< Row count */
    paramset_size = 1;	/**< SQL_ATTR_PARAMSET_SIZE */
    paramset_count = 0;	/**< Internal for paramset */
    paramset_nrows = 0;	/**< Row count for paramset handling */
    max_rows = 0;	/**< SQL_ATTR_MAX_ROWS */
    bind_type = SQL_BIND_BY_COLUMN;	/**< SQL_ATTR_ROW_BIND_TYPE */
    bind_offs = NULL;	/**< SQL_ATTR_ROW_BIND_OFFSET_PTR */
    /* Dummies to make ADO happy */
    parm_bind_offs = NULL;/**< SQL_ATTR_PARAM_BIND_OFFSET_PTR */
    parm_oper = NULL;	/**< SQL_ATTR_PARAM_OPERATION_PTR */
    parm_status = NULL;	/**< SQL_ATTR_PARAMS_STATUS_PTR */
    parm_proc = NULL;	/**< SQL_ATTR_PARAMS_PROCESSED_PTR */
    parm_bind_type = SQL_PARAM_BIND_BY_COLUMN;	/**< SQL_ATTR_PARAM_BIND_TYPE */
    curtype = m_pDbc->curtype;
    engstmt_noreset = 0;		/**< False when sqlite3_reset() needed. */
    engstmt_rownum = 0;		/**< Current row number */
    bincell = NULL;		/**< Cache for blob data */
    bincache = NULL;		/**< Cache for blob data */
    binlen = 0;			/**< Length of blob data */
    m_guessed_types = 0;		/**< Flag for drv_prepare()/drvexecute() */
	m_ppCol= NULL;
	m_ppVal = NULL;
	m_bArrayMode = FALSE;
	m_nArrIdx = 0;
	//==================================================
	// 통신용 데이터 초기화.
	//==================================================
	InitStmt();
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 파괴자.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
CSTMT::~CSTMT(void)
{
	FreeDynCols();
	FreeBindCols();
	DeleteCols();
	XFREE(m_ppCol)
	XFREE(m_ppVal)
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 클래스 생성 및 삭제.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 데이터 초기화.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::InitStmt()
{
	XFREE(m_ppCol);
	XFREE(m_ppVal);
	m_ppVal = NULL;
	memset(m_szErr,0,128);
	m_nRecCount = 0;
	m_nColCount = 0;
	m_nIsEnd = ENG_ERROR;
	m_nRemain = 0;
	m_nPos = 0;
	m_nReply = 0;
	m_nStat = 0;
	memset(m_szData,0,DATA_SIZE + 1);
	RESETPACK(m_fetchPack);
	m_nfetchPos = 0;
	m_nMoveNext =ENG_OK;
	m_nFetchStatEnd = ENG_OK ;
	m_nAdd = 0; 
	m_bMoveNextEnd = TRUE;
	m_nFillSize = 0;
	m_nReadSize = 0; 
	m_bUse = TRUE;
	m_pnAuth = &m_pDbc->m_nAuth;
	m_pPack = &m_pDbc->m_Pack;
	m_bArrayMode = FALSE;
	m_nArrIdx = 0;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 재사용을 위한 함수.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Reset()
{
	m_bUse = FALSE;
	return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 원래는 없애는 함수이나, 실제로 재사용을 위해서 준비.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Finalize()
{
	m_bUse = FALSE;
	return SQL_SUCCESS;
}
void CSTMT::SetErr(char * pErr)
{
	memset(m_szErr,0,128);
	STRCPY(m_szErr,pErr,128);
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Cols 삭제.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::DeleteCols()
{
	/*
	if( NULL == m_pCols)
		return;
	for( int i = 0 ; i < m_nColCount ; i ++)
	{
		if( NULL == m_pCols[i].ptypename)
		{
			free(m_pCols[i].ptypename);
			m_pCols[i].ptypename = NULL;
		}
	}
	free(m_pCols);
	m_pCols = NULL;
	*/
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 메모리 없음.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::NoMemory()
{
	SetState(-1, "Out of memory", g_ov3 ? "HY000" : "S1000");
    return SQL_ERROR;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 상태 설정.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::SetState(int naterr, char *msg, char *st, ...)
{
    va_list ap;
    naterr = naterr;
    logmsg[0] = '\0';
    if (msg) 
	{
		int count;
		va_start(ap, st);
		count = vsnprintf((char *) logmsg, sizeof (logmsg), msg, ap);
		va_end(ap);
		if (count < 0) 
		{
			logmsg[sizeof (logmsg) - 1] = '\0';
		}
    }
    if (!st) 
	{
		st = "?????";
    }
    strncpy_s(sqlstate,6, st, 5);
    sqlstate[5] = '\0';
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Select를 수행한다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
BOOL CSTMT::Prepare(const char * pszSQL)
{
	return TRUE;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 테이블 얻기.(배열로 얻기.)
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
BOOL CSTMT::GetTable(const char *pszSQL)
{
	int nCount = 0;
	if( TRUE == Prepare(pszSQL))
	{
		int i;
		int nLen;
		for(i = 0 ; i < m_nColCount ; i ++ )
		{
			const char *pValue = GetFieldName(i);
			ArrAddAlloc(nCount,pValue);
			nCount ++;
		}
		m_nRowCount ++;
		MoveNext();
		while(FALSE == m_bMoveNextEnd)
		{
			for(i = 0 ; i < m_nColCount ; i ++ )
			{
				const char *pValue = Get_Column_Text(i);
				ArrAddAlloc(nCount,pValue);
				nCount ++;
			}
			MoveNext();
			m_nRowCount ++;
		}
		m_bArrayMode = TRUE;
		m_bMoveNextEnd = FALSE;
		return TRUE;
	}
	else
	{
		m_nRowCount = 0;
		m_nColCount = 0;
		return FALSE;
	}
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Execute를 수행 한다. 사실은 Step 과 마찬가지.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Execute()
{
//	MoveNext();	
	return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 컬럼 카운트 가져온다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
short CSTMT::GetFieldCount()
{
	return 0;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 컬럼 정보를 가져 온다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::GetColumnInfo(const short nIndex)
{

}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 필드 이름 받기.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const char * CSTMT::GetFieldName(const short nIndex)
{
	return NULL;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 데이터의 끝인가?
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
BOOL CSTMT::IsEOF()
{
	return  m_bMoveNextEnd;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 텍스트 형태로 데이터 가져 오기.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const char * CSTMT::Get_Column_Text(const int nIndex)
{
	if (( nIndex >= 0 ) && ( nIndex <  m_nColCount))
	{
		return (const char *)(m_ppVal[nIndex]+1);
	}
	return NULL;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Move Next.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::MoveNext()
{
	m_bMoveNextEnd = TRUE;
	int i = 0;
	for(i = 0 ; i < m_nColCount ; i ++)
	{
		memset(m_ppVal[i],'\0',sizeof(FIELD_BUF));
		m_nAdd  = 0;
		while(ENG_ERROR == MoveNextCore(i))
		{
		}
	}
	if(m_bMoveNextEnd == FALSE)
		return ENG_ROW;
	else
		return ENG_DONE;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Fetch를 수행 한다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Fetch()
{
	if(ENG_DONE == MoveNext())
	{
		return SQL_NO_DATA;
	}
	else
	{
		DoFetchBind();
		return SQL_SUCCESS;
	}
	/*
	}
	else
	{
		if( m_nArrIdx < m_arr.GetCount())
		{
			DoFetchBind();
			m_nArrIdx += m_nColCount; // 인덱스를 옮긴다.
			return SQL_SUCCESS;
		}
		else
		{
			return SQL_NO_DATA;
		}
	}
	*/
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 실제적으로 데이터를 채운다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::MoveNextCore(const int nIndex)
{
	while( m_nfetchPos < m_fetchPack.nDataLen)
	{
		m_bMoveNextEnd = FALSE;
		if( '\0' == m_fetchPack.szData[m_nfetchPos] )
		{
			m_nfetchPos ++;
			return ENG_OK;
		}
		else
		{
			m_ppVal[nIndex][m_nAdd] = m_fetchPack.szData[m_nfetchPos];
			m_nAdd ++;
			m_nfetchPos ++;
		}
	}
	if((ENG_ERROR == m_nFetchStatEnd) || ( ENG_ERROR == m_nIsEnd))
	{
			FetchProcess();
			return ENG_ERROR;
	}
	else
		return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 서버로부터 데이터를 가져와 버퍼를 채운다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::FetchProcess()
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Bind 컬럼.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::GetBindParmeterCount()
{
	return 0;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 레코드 카운트롤 넘긴다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::GetRecCount()
{
	return m_nRecCount;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 컬럼 카운트 리턴.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Column_Count()
{
	return m_nColCount;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 버퍼의 컬럼 이름 리턴.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const char *CSTMT::Get_Column_Name(int N)
{
	if((N < 0) || (m_nColCount <= N))
	{
		return SPACECHAR;
	}
	return m_ppCol[N].szColumnName;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 정의된 컬럼 Type 이름 리턴.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const char *CSTMT::Get_Column_Decltype(int N)
{
	if((N < 0) || (m_nColCount <= N))
	{
		return SPACECHAR;
	}
	if( strlen(m_ppCol[N].szTypename) < 1 )
		return NULL;
	else 
		return m_ppCol[N].szTypename;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 정의된 컬럼 Type 리턴.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Column_Type(int N)
{
	if((N < 0) || (m_nColCount <= N))
	{
		return 0;
	}
	return m_ppVal[N][0];
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 바이트 얻기.(빈함수.)
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Column_Bytes(int iCol)
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 바이너리 함수 얻기.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const void *CSTMT::Get_Column_Blob(int iCol)
{
	return NULL;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 널 얻기?
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Null(int iCol)
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 텍스트 Bind
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Text(int iCol, const char* p, int n, void(*)(void*))
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Blob Bind
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Blob(int iCol, const void* p, int n, void(*)(void*))
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Double Bind
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Double(int iCol, double dl)
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Int64 Bind
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Int64(int iCol, __int64 n64)
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Int Bind
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Int(int iCol , int myInt)
{
	return ENG_OK;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Step
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Step()
{
	return MoveNext();
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Bind 컬럼.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::Get_Bind_Parameter_Count()
{
	return 0;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 컬럼 정보
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const COLUMN_INFO * CSTMT::Get_Column_Info(const int nCol)
{
	if(( 0 <= nCol ) && ( nCol < m_nColCount))
	{
		return (m_ppCol + nCol);
	}
	else
		return NULL;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 컬럼 설정 정보
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Dynamic Colummn 설정 - Type 미치 기타 정보 설정.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
BOOL CSTMT::SetupDynCols()
{
    int guessed_types = 0;
    SQLRETURN ret = SQL_SUCCESS;
	//==================================================
	// 컬럼이 0 보다 크다.
	//==================================================
	if( m_nColCount < 0)
		return FALSE;
	int i;
	const char *ptypename;
	FreeDynCols();
	XMALLOC(m_pDynCols,COL*,m_nColCount * sizeof(COL))
	for (i = 0; i < m_nColCount; i++) 
	{
		if (m_pDbc->trace) 
		{
			fprintf(m_pDbc->trace, "-- column %d name: '%s'\n",i + 1, Get_Column_Name(i));
			fflush(m_pDbc->trace);
		}
		ptypename = StmtColType(i,&guessed_types);
		m_pDynCols[i].db = m_pDbc->dbname;
		m_pDynCols[i].column = m_ppCol[i].szColumnName;
		m_pDynCols[i].label = m_ppCol[i].szLabel;
		m_pDynCols[i].table = m_ppCol[i].szTableName;
		if (longnames) 
		{
		    m_pDynCols[i].column = m_pDynCols[i].label;
		}
#ifdef SQL_LONGVARCHAR
		m_pDynCols[i].type = SQL_LONGVARCHAR;
		m_pDynCols[i].size = 65535;
#else
		m_pDynCols[i].type = SQL_VARCHAR;
		m_pDynCols[i].size = 255;
#endif
		m_pDynCols[i].index = i;
		m_pDynCols[i].scale = 0;
		m_pDynCols[i].prec = 0;
		m_pDynCols[i].nosign = 1;
#if defined(HAVE_SQLITE3TABLECOLUMNMETADATA) && HAVE_SQLITE3TABLECOLUMNMETADATA
		s3stmt_addmeta(s3stmt, i, d, &m_pDynCols[i]);
#else
		m_pDynCols[i].autoinc = SQL_FALSE;
		m_pDynCols[i].notnull = SQL_NULLABLE;
#endif
		m_pDynCols[i].ptypename = strdup_(ptypename);
	}
	m_nDcols = m_nColCount;
	m_guessed_types = guessed_types;
	FixupDynCols();
	return TRUE;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// DynClos에 대한 메모리 삭제.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::FreeDynCols()
{
    if (NULL != m_pDynCols) 
	{
		int i;
		for (i = 0; i < m_nDcols; i++) 
		{
			XFREE(m_pDynCols[i].ptypename);
		}
		XFREE(m_pDynCols);
    }
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Bind Cols 에 대한 메모리 삭제.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::FreeBindCols()
{
    if (NULL != m_pBindCols) 
	{
		XFREE(m_pBindCols);
	}
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// stmt ColType 결정. - 원래 Type 결정이 아님.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
const char * CSTMT::StmtColType(int col, int *guessed_types)
{
	char *ptypename = (char *)Get_Column_Decltype(col);
    char guess[64];
    guess[0] = '\0';
	if (!ptypename) 
	{
		int coltype = Get_Column_Type(col);
		if (guessed_types) 
		{
			guessed_types[0]++;
		}
		if (m_pDbc->trace) 
		{
			sprintf(guess, " (guessed from %d)", coltype);
		}
		switch (coltype) 
		{
			case SQLITE_INTEGER: ptypename = "integer"; break;
			case SQLITE_FLOAT:   ptypename = "double";  break;
			default:
			case SQLITE_TEXT:    ptypename = "varchar"; break;
			case SQLITE_BLOB:    ptypename = "blob";    break;
#if 0
			case SQLITE_NULL:    ptypename = "null";    break;
#endif
		}
    }
    if (m_pDbc->trace) 
	{
		fprintf(m_pDbc->trace, "-- column %d type%s: '%s'\n", col + 1,guess, ptypename);
		fflush(m_pDbc->trace);
    }
    return ptypename;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// DynCols 결정 
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::FixupDynCols()
{
    int i;
#if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA
    char *flagp, flags[128];
#endif

    if (!m_pDynCols) 
	{
		return;
    }
    /* fixup labels */
    if (!longnames) 
	{
		if (m_nDcols > 1) 
		{
			char *table = m_pDynCols[0].table;
		    for (i = 1; table[0] && i < m_nDcols; i++) 
			{
				if (strcmp(m_pDynCols[i].table, table)) 
				{
					break;
				}
			}
			if (i >= m_nDcols) 
			{
				for (i = 0; i < m_nDcols; i++) 
				{
					m_pDynCols[i].label = m_pDynCols[i].column;
				}
			}
		} 
		else if (m_nDcols == 1) 
		{
			m_pDynCols[0].label = m_pDynCols[0].column;
		}
    }
    for (i = 0; i < m_nDcols; i++) 
	{
		m_pDynCols[i].type =
	    mapsqltype(m_pDynCols[i].ptypename, &m_pDynCols[i].nosign, g_ov3, nowchar[0] || nowchar[1], dobigint);
		getmd(m_pDynCols[i].ptypename, m_pDynCols[i].type,
	      &m_pDynCols[i].size, &m_pDynCols[i].prec);
#ifdef SQL_LONGVARCHAR
		if (m_pDynCols[i].type == SQL_VARCHAR &&
	    m_pDynCols[i].size > 255) 
		{
			m_pDynCols[i].type = SQL_LONGVARCHAR;
		}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
	if (m_pDynCols[i].type == SQL_WVARCHAR &&
	    m_pDynCols[i].size > 255) {
	    m_pDynCols[i].type = SQL_WLONGVARCHAR;
	}
#endif
#endif
		if (m_pDynCols[i].type == SQL_VARBINARY &&
			m_pDynCols[i].size > 255) 
		{
			m_pDynCols[i].type = SQL_LONGVARBINARY;
		}
    }
#if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA
    if (m_nDcols > array_size(flags)) 
	{
		XMALLOC(flagp,char*,sizeof(flags[0]) * m_nDcols)
		if (flagp == NULL) 
		{
			return;
		}
    } 
	else 
	{
		flagp = flags;
    }
    memset(flagp, 0, sizeof (flags[0]) * m_nDcols);
    for (i = 0; i < m_nDcols; i++) 
	{
		m_pDynCols[i].autoinc = SQL_FALSE;
		m_pDynCols[i].notnull = SQL_NULLABLE;
    }
    if (flagp != flags) 
	{
		XFREE(flagp);
    }
#endif
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Get Data
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::GetRowData(SQLUSMALLINT col, SQLSMALLINT otype, SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, int partial)
{
    char *data, valdummy[16];
    SQLLEN dummy;
    int valnull = 0;
    int type = otype;

    if (!lenp) 
	{
		lenp = &dummy;
    }
    if (m_bMoveNextEnd) 
	{
		*lenp = SQL_NULL_DATA;
		return SQL_NO_DATA;
    }
    if (col >= m_nColCount) 
	{
		SetState( -1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (retr_data != SQL_RD_ON) 
	{
		return SQL_SUCCESS;
    }
    type = mapdeftype(type, m_pDynCols[col].type, m_pDynCols[col].nosign ? 1 : 0, nowchar[0]);
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
    /* MS Access hack part 3 (map SQL_C_DEFAULT to SQL_C_CHAR) */
    if (type == SQL_C_WCHAR && otype == SQL_C_DEFAULT) 
	{
		type = SQL_C_CHAR;
    }
#endif
	data = &m_ppVal[col][1];
    if (!val) 
	{
		valnull = 1;
		val = (SQLPOINTER) valdummy;
    }
    if (data == NULL) 
	{
	*lenp = SQL_NULL_DATA;
	switch (type) {
	case SQL_C_UTINYINT:
	case SQL_C_TINYINT:
	case SQL_C_STINYINT:
#ifdef SQL_BIT
	case SQL_C_BIT:
#endif
	    *((char *) val) = 0;
	    break;
	case SQL_C_USHORT:
	case SQL_C_SHORT:
	case SQL_C_SSHORT:
	    *((short *) val) = 0;
	    break;
	case SQL_C_ULONG:
	case SQL_C_LONG:
	case SQL_C_SLONG:
	    *((SQLINTEGER *) val) = 0;
	    break;
#ifdef SQL_BIGINT
	case SQL_C_SBIGINT:
	case SQL_C_UBIGINT:
	    *((SQLBIGINT *) val) = 0;
	    break;
#endif
	case SQL_C_FLOAT:
	    *((float *) val) = 0;
	    break;
	case SQL_C_DOUBLE:
	    *((double *) val) = 0;
	    break;
	case SQL_C_BINARY:
	case SQL_C_CHAR:
	    *((char *) val) = '\0';
	    break;
#ifdef WCHARSUPPORT
	case SQL_C_WCHAR:
	    *((SQLWCHAR *) val) = '\0';
	    break;
#endif
#ifdef SQL_C_TYPE_DATE
	case SQL_C_TYPE_DATE:
#endif
	case SQL_C_DATE:
	    memset((DATE_STRUCT *) val, 0, sizeof (DATE_STRUCT));
	    break;
#ifdef SQL_C_TYPE_TIME
	case SQL_C_TYPE_TIME:
#endif
	case SQL_C_TIME:
	    memset((TIME_STRUCT *) val, 0, sizeof (TIME_STRUCT));
	    break;
#ifdef SQL_C_TYPE_TIMESTAMP
	case SQL_C_TYPE_TIMESTAMP:
#endif
	case SQL_C_TIMESTAMP:
	    memset((TIMESTAMP_STRUCT *) val, 0, sizeof (TIMESTAMP_STRUCT));
	    break;
	default:
	    return SQL_ERROR;
	}
    } else {
	char *endp = NULL;
#if defined(_WIN32) || defined(_WIN64)
#ifdef SQL_BIGINT
	char endc;
#endif
#endif

	switch (type) {
	case SQL_C_UTINYINT:
	case SQL_C_TINYINT:
	case SQL_C_STINYINT:
	    *((char *) val) = (char)strtol(data, &endp, 0);
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (char);
	    }
	    break;
#ifdef SQL_BIT
	case SQL_C_BIT:
	    *((char *) val) = getbool(data);
	    *lenp = sizeof (char);
	    break;
#endif
	case SQL_C_USHORT:
	case SQL_C_SHORT:
	case SQL_C_SSHORT:
	    *((short *) val) = strtol(data, &endp, 0);
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (short);
	    }
	    break;
	case SQL_C_ULONG:
	case SQL_C_LONG:
	case SQL_C_SLONG:
	    *((SQLINTEGER *) val) = strtol(data, &endp, 0);
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (SQLINTEGER);
	    }
	    break;
#ifdef SQL_BIGINT
	case SQL_C_UBIGINT:
#if defined(_WIN32) || defined(_WIN64)
	    if (sscanf(data, "%I64u%c", (SQLUBIGINT *) val, &endc) != 1) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (SQLUBIGINT);
	    }
#else
#ifdef __osf__
	    *((SQLUBIGINT *) val) = strtoul(data, &endp, 0);
#else
	    *((SQLUBIGINT *) val) = strtoull(data, &endp, 0);
#endif
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (SQLUBIGINT);
	    }
#endif
	    break;
	case SQL_C_SBIGINT:
#if defined(_WIN32) || defined(_WIN64)
	    if (sscanf(data, "%I64d%c", (SQLBIGINT *) val, &endc) != 1) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (SQLBIGINT);
	    }
#else
#ifdef __osf__
	    *((SQLBIGINT *) val) = strtol(data, &endp, 0);
#else
	    *((SQLBIGINT *) val) = strtoll(data, &endp, 0);
#endif
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (int);
	    }
#endif
	    break;
#endif
	case SQL_C_FLOAT:
	    *((float *) val) = ln_strtod(data, &endp);
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (float);
	    }
	    break;
	case SQL_C_DOUBLE:
	    *((double *) val) = ln_strtod(data, &endp);
	    if (endp && endp == data) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (double);
	    }
	    break;
	case SQL_C_BINARY: {
	    int dlen, offs = 0;
	    char *bin;

	    if (valnull) {
		XFREE(bincache);
		binlen = 0;
		goto doCHAR;
	    }
	    if (data == bincell) {
		if (bincache) {
		    bin = bincache;
		    dlen = binlen;
		} else {
		    goto doCHAR;
		}
	    } else {
		char *dp;
		int i;

		XFREE(bincache);
		dp = data;
		dlen = strlen(dp);
		bincell = dp;
		binlen = 0;
		if (!(dp[0] == 'x' || dp[0] == 'X') || dp[1] != '\'' ||
		    dp[dlen - 1] != '\'') {
		    goto doCHAR;
		}
		dlen -= 2;
		dp += 2;
		dlen = dlen / 2;
		//bin = xmalloc(dlen);
		XMALLOC(bin,char *,dlen)
		bincache = bin;
		if (!bin) {
		    return NoMemory();
		}
		binlen = dlen;
		memset(bincache, 0, dlen);
		for (i = 0; i < dlen; i++) {
		    char *x;
		    int v;

		    if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
			goto converr;
		    }
		    v = x - xdigits;
		    bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4);
		    ++dp;
		    if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
converr:
			XFREE(bincache);
			binlen = 0;
			SetState( -1, "conversion error",
				g_ov3 ? "HY000" : "S1000");
			return SQL_ERROR;
		    }
		    v = x - xdigits;
		    bin[i] |= (v >= 16) ? (v - 6) : v;
		    ++dp;
		}
		bin = bincache;
	    }
	    if (partial && len && m_pBindCols) {
		if (m_pBindCols[col].offs >= dlen) {
		    *lenp = 0;
		    if (!dlen && m_pBindCols[col].offs == dlen) {
			m_pBindCols[col].offs = 1;
			return SQL_SUCCESS;
		    }
		    m_pBindCols[col].offs = 0;
		    return SQL_NO_DATA;
		}
		offs = m_pBindCols[col].offs;
		dlen -= offs;
	    }
	    if (val && len) {
		memcpy(val, bin + offs, min(len, dlen));
	    }
	    if (len < 1) {
		*lenp = dlen;
	    } else {
		*lenp = min(len, dlen);
		if (*lenp == len && *lenp != dlen) {
		    *lenp = SQL_NO_TOTAL;
		}
	    }
	    if (partial && len && m_pBindCols) {
		if (*lenp == SQL_NO_TOTAL) {
		    *lenp = dlen;
		    m_pBindCols[col].offs += len;
		    SetState( -1, "data right truncated", "01004");
		    if (m_pBindCols[col].lenp) {
			*m_pBindCols[col].lenp = dlen;
		    }
		    return SQL_SUCCESS_WITH_INFO;
		}
		m_pBindCols[col].offs += *lenp;
	    }
	    if (*lenp == SQL_NO_TOTAL) {
		*lenp = dlen;
		SetState( -1, "data right truncated", "01004");
		return SQL_SUCCESS_WITH_INFO;
	    }
	    break;
	}
	doCHAR:
#ifdef WCHARSUPPORT
	case SQL_C_WCHAR:
#endif
	case SQL_C_CHAR: {
	    int doz, zlen = len - 1;
	    int dlen = strlen(data);
	    int offs = 0;
#ifdef WCHARSUPPORT
	    SQLWCHAR *ucdata = NULL;
	    SQLCHAR *cdata = (SQLCHAR *) data;
#endif

#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
	    /* MS Access hack part 2 (reserved error -7748) */
	    if (!valnull &&
		(m_pDynCols == statSpec2P || m_pDynCols == statSpec3P) &&
		type == SQL_C_WCHAR) {
		if (len > 0 && len <= sizeof (SQLWCHAR)) {
		    ((char *) val)[0] = data[0];
		    memset((char *) val + 1, 0, len - 1);
		    *lenp = 1;
		    return SQL_SUCCESS;
		}
	    }
#endif

#ifdef WCHARSUPPORT
	    switch (type) {
	    case SQL_C_CHAR:
		doz = 1;
		break;
	    case SQL_C_WCHAR:
		doz = sizeof (SQLWCHAR);
		break;
	    default:
		doz = 0;
		break;
	    }
	    if (type == SQL_C_WCHAR) 
		{
			ucdata = uc_from_utf(cdata, dlen);
			if (!ucdata) 
			{
				return NoMemory();
			}
			dlen = uc_strlen(ucdata) * sizeof (SQLWCHAR);
	    }
#if defined(_WIN32) || defined(_WIN64)
	    else if (*oemcp && type == SQL_C_CHAR) 
		{
			ucdata = (SQLWCHAR *) utf_to_wmb((char *) cdata, dlen);
			if (!ucdata) 
			{
				return NoMemory();
			}
			cdata = (SQLCHAR *) ucdata;
			dlen = strlen((char *) cdata);
	    }
#endif
#else
	    doz = (type == SQL_C_CHAR) ? 1 : 0;
#endif
	    if (partial && len && m_pBindCols) {
		if (m_pBindCols[col].offs >= dlen) {
#ifdef WCHARSUPPORT
		    XFREE(ucdata);
#endif
		    *lenp = 0;
		    if (doz && val) {
#ifdef WCHARSUPPORT
			if (type == SQL_C_WCHAR) {
			    ((SQLWCHAR *) val)[0] = 0;
			} else {
			    ((char *) val)[0] = '\0';
			}
#else
			((char *) val)[0] = '\0';
#endif
		    }
		    if (!dlen && m_pBindCols[col].offs == dlen) {
			m_pBindCols[col].offs = 1;
			return SQL_SUCCESS;
		    }
		    m_pBindCols[col].offs = 0;
		    return SQL_NO_DATA;
		}
		offs = m_pBindCols[col].offs;
		dlen -= offs;
	    }
	    if (val && !valnull && len) {
#ifdef WCHARSUPPORT
		if (type == SQL_C_WCHAR) {
		    uc_strncpy((SQLWCHAR*)val, ucdata + offs / sizeof (SQLWCHAR),
			       (len - doz) / sizeof (SQLWCHAR));
		} else {
		    strncpy((char*)val, (char *) cdata + offs, len - doz);
		}
#else
		strncpy(val, data + offs, len - doz);
#endif
	    }
	    if (valnull || len < 1) {
		*lenp = dlen;
	    } else {
		*lenp = min(len - doz, dlen);
		if (*lenp == len - doz && *lenp != dlen) {
		    *lenp = SQL_NO_TOTAL;
		} else if (*lenp < zlen) {
		    zlen = *lenp;
		}	    
	    }
	    if (len && !valnull && doz) {
#ifdef WCHARSUPPORT
		if (type == SQL_C_WCHAR) {
		    ((SQLWCHAR *) val)[zlen / sizeof (SQLWCHAR)] = 0;
		} else {
		    ((char *) val)[zlen] = '\0';
		}
#else
		((char *) val)[zlen] = '\0';
#endif
	    }
#ifdef WCHARSUPPORT
	    XFREE(ucdata);
#endif
	    if (partial && len && m_pBindCols) {
		if (*lenp == SQL_NO_TOTAL) {
		    *lenp = dlen;
		    m_pBindCols[col].offs += len - doz;
		    SetState( -1, "data right truncated", "01004");
		    if (m_pBindCols[col].lenp) {
			*m_pBindCols[col].lenp = dlen;
		    }
		    return SQL_SUCCESS_WITH_INFO;
		}
		m_pBindCols[col].offs += *lenp;
	    }
	    if (*lenp == SQL_NO_TOTAL) {
		*lenp = dlen;
		SetState( -1, "data right truncated", "01004");
		return SQL_SUCCESS_WITH_INFO;
	    }
	    break;
	}
#ifdef SQL_C_TYPE_DATE
	case SQL_C_TYPE_DATE:
#endif
	case SQL_C_DATE:
	    if (str2date(data, (DATE_STRUCT *) val) < 0) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (DATE_STRUCT);
	    }
	    break;
#ifdef SQL_C_TYPE_TIME
	case SQL_C_TYPE_TIME:
#endif
	case SQL_C_TIME:
	    if (str2time(data, (TIME_STRUCT *) val) < 0) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (TIME_STRUCT);
	    }
	    break;
#ifdef SQL_C_TYPE_TIMESTAMP
	case SQL_C_TYPE_TIMESTAMP:
#endif
	case SQL_C_TIMESTAMP:
	    if (str2timestamp(data, (TIMESTAMP_STRUCT *) val) < 0) {
		*lenp = SQL_NULL_DATA;
	    } else {
		*lenp = sizeof (TIMESTAMP_STRUCT);
	    }
	    switch (m_pDynCols[col].prec) {
	    case 0:
		((TIMESTAMP_STRUCT *) val)->fraction = 0;
		break;
	    case 1:
		((TIMESTAMP_STRUCT *) val)->fraction /= 100000000;
		((TIMESTAMP_STRUCT *) val)->fraction *= 100000000;
		break;
	    case 2:
		((TIMESTAMP_STRUCT *) val)->fraction /= 10000000;
		((TIMESTAMP_STRUCT *) val)->fraction *= 10000000;
		break;
	    }
	    break;
	default:
	    return SQL_ERROR;
	}
    }
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Get Data
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::DescribeCol(SQLUSMALLINT col, SQLCHAR * name,
	       SQLSMALLINT nameMax, SQLSMALLINT *nameLen,
	       SQLSMALLINT *type, SQLULEN *size,
	       SQLSMALLINT *digits, SQLSMALLINT *nullable)
{
    int didname = 0;
	if( 0 >= m_nColCount)
	{
		SetState( -1, "no columns", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (col < 1 || col > m_nColCount) 
	{
		SetState(-1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
	col--;
    if (name && nameMax > 0) 
	{
		//util_UTF2UC(c->column,name,nameMax);
		strncpy((char *) name, m_ppCol[col].szColumnName, nameMax);
		name[nameMax - 1] = '\0';
		didname = 1;
    }
    if (nameLen) 
	{
		if (didname) 
		{
			*nameLen = strlen((char *) name);
		} 
		else 
		{
			*nameLen = strlen(m_ppCol[col].szColumnName);
		}
    }
	if (nowchar[0] || nowchar[1]) 
	{
	    switch (m_ppCol[col].nType) 
		{
		case SQL_WCHAR:
			*type = SQL_CHAR;
			break;
		case SQL_WVARCHAR:
			*type = SQL_VARCHAR;
			break;
		case SQL_WLONGVARCHAR:
			*type = SQL_LONGVARCHAR;
			break;
		}
	}
    if (size) 
	{
		*size = m_pDynCols[col].size;
    }
    if (digits) 
	{
		*digits = 0;
    }
    if (nullable) 
	{
		*nullable = 1;
    }
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Get Table에 대한 것. - 메모리 및 메타 부분.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Type정보를 가져 온다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::GetTypeInfo(SQLSMALLINT sqltype)
{
	SQLRETURN ret;
    int asize;
    ret = MakeResultSet(typeSpec2, array_size(typeSpec2),
		      typeSpec3, array_size(typeSpec3), &asize);
    if (ret != SQL_SUCCESS) 
	{
		return ret;
    }
	m_nArrIdx = 0;
#ifdef SQL_LONGVARCHAR
    m_nRowCount = (sqltype == SQL_ALL_TYPES) ? 13 : 1;
#else
    m_nRowCount = (sqltype == SQL_ALL_TYPES) ? 12 : 1;
#endif
    if (sqltype == SQL_ALL_TYPES) 
	{
#ifdef WINTERFACE
		m_nRowCount += 2;
#ifdef SQL_WLONGVARCHAR
		m_nRowCount += 2;
#endif
#endif
    }
    if (sqltype == SQL_ALL_TYPES) 
	{
		m_nRowCount += 2;
#ifdef SQL_BIT
		m_nRowCount += 1;
#endif
#ifdef SQL_BIGINT
		m_nRowCount += 1;
#endif
    }
#ifdef MEMORY_DEBUG
    rowfree = XFREE__;
#else
    //rowfree = sqlite3_free;
	nrowfreetype = FREE_TYPE_ENGFREE;
#endif
    if (sqltype == SQL_ALL_TYPES) 
	{
		int cc = 1;
		MakeTypeInfo( cc++, asize, "varchar", SQL_VARCHAR, 0);
		MakeTypeInfo( cc++, asize, "tinyint", SQL_TINYINT, 0);
		MakeTypeInfo( cc++, asize, "smallint", SQL_SMALLINT, 0);
		MakeTypeInfo( cc++, asize, "integer", SQL_INTEGER, 0);
		MakeTypeInfo( cc++, asize, "float", SQL_FLOAT, 0);
		MakeTypeInfo( cc++, asize, "double", SQL_DOUBLE, 0);
#ifdef SQL_TYPE_DATE
		MakeTypeInfo( cc++, asize, "date", g_ov3 ? SQL_TYPE_DATE : SQL_DATE, 0);
#else
		MakeTypeInfo( cc++, asize, "date", SQL_DATE, 0);
#endif
#ifdef SQL_TYPE_TIME
		MakeTypeInfo( cc++, asize, "time", g_ov3 ? SQL_TYPE_TIME : SQL_TIME, 0);
#else
		MakeTypeInfo( cc++, asize, "time", SQL_TIME, 0);
#endif
#ifdef SQL_TYPE_TIMESTAMP
		MakeTypeInfo( cc++, asize, "timestamp",
		   g_ov3 ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP, 0);
#else
		MakeTypeInfo( cc++, asize, "timestamp", SQL_TIMESTAMP, 0);
#endif
		MakeTypeInfo( cc++, asize, "char", SQL_CHAR, 0);
		MakeTypeInfo( cc++, asize, "numeric", SQL_DOUBLE, 0);
#ifdef SQL_LONGVARCHAR
		MakeTypeInfo( cc++, asize, "text", SQL_LONGVARCHAR, 0);
		MakeTypeInfo( cc++, asize, "longvarchar", SQL_LONGVARCHAR, 0);
#else
	MakeTypeInfo( cc++, asize, "text", SQL_VARCHAR, 0);
#endif
	MakeTypeInfo( cc++, asize, "varbinary", SQL_VARBINARY, 0);
	MakeTypeInfo( cc++, asize, "longvarbinary", SQL_LONGVARBINARY, 0);
#ifdef SQL_BIT
	MakeTypeInfo( cc++, asize, "bit", SQL_BIT, 0);
#endif
#ifdef SQL_BIGINT
	MakeTypeInfo( cc++, asize, "bigint", SQL_BIGINT, 0);
#endif
#ifdef WINTERFACE
	MakeTypeInfo( cc++, asize, "wvarchar", SQL_WVARCHAR, 0);
	MakeTypeInfo( cc++, asize, "wchar", SQL_WCHAR, 0);
#ifdef SQL_WLONGVARCHAR
	MakeTypeInfo( cc++, asize, "wtext", SQL_WLONGVARCHAR, 0);
	MakeTypeInfo( cc++, asize, "longwvarchar", SQL_WLONGVARCHAR, 0);
#endif
#endif
	/*qsort(m_arr[asize], m_nRowCount, sizeof (char *) * asize,
	      typeinfosort);
		  */
    } 
	else 
	{
	switch (sqltype) {
	case SQL_CHAR:
	    MakeTypeInfo( 1, asize, "char", SQL_CHAR, 10);
	    break;
	case SQL_VARCHAR:
	    MakeTypeInfo( 1, asize, "varchar", SQL_VARCHAR, 1);
	    break;
	case SQL_TINYINT:
	    MakeTypeInfo( 1, asize, "tinyint", SQL_TINYINT, 2);
	    break;
	case SQL_SMALLINT:
	    MakeTypeInfo( 1, asize, "smallint", SQL_SMALLINT, 3);
	    break;
	case SQL_INTEGER:
	    MakeTypeInfo( 1, asize, "integer", SQL_INTEGER, 4);
	    break;
	case SQL_FLOAT:
	    MakeTypeInfo( 1, asize, "float", SQL_FLOAT, 5);
	    break;
	case SQL_DOUBLE:
	    MakeTypeInfo( 1, asize, "double", SQL_DOUBLE, 6);
	    break;
#ifdef SQL_TYPE_DATE
	case SQL_TYPE_DATE:
	    MakeTypeInfo( 1, asize, "date", SQL_TYPE_DATE, 25);
	    break;
#endif
	case SQL_DATE:
	    MakeTypeInfo( 1, asize, "date", SQL_DATE, 7);
	    break;
#ifdef SQL_TYPE_TIME
	case SQL_TYPE_TIME:
	    MakeTypeInfo( 1, asize, "time", SQL_TYPE_TIME, 26);
	    break;
#endif
	case SQL_TIME:
	    MakeTypeInfo( 1, asize, "time", SQL_TIME, 8);
	    break;
#ifdef SQL_TYPE_TIMESTAMP
	case SQL_TYPE_TIMESTAMP:
	    MakeTypeInfo( 1, asize, "timestamp", SQL_TYPE_TIMESTAMP, 27);
	    break;
#endif
	case SQL_TIMESTAMP:
	    MakeTypeInfo( 1, asize, "timestamp", SQL_TIMESTAMP, 9);
	    break;
#ifdef SQL_LONGVARCHAR
	case SQL_LONGVARCHAR:
	    MakeTypeInfo( 1, asize, "longvarchar", SQL_LONGVARCHAR, 12);
	    break;
#endif
	case SQL_VARBINARY:
	    MakeTypeInfo( 1, asize, "varbinary", SQL_VARBINARY, 30);
	    break;
	case SQL_LONGVARBINARY:
	    MakeTypeInfo( 1, asize, "longvarbinary", SQL_LONGVARBINARY, 31);
	    break;
#ifdef SQL_BIT
	case SQL_BIT:
	    MakeTypeInfo( 1, asize, "bit", SQL_BIT, 29);
	    break;
#endif
#ifdef SQL_BIGINT
	case SQL_BIGINT:
	    MakeTypeInfo( 1, asize, "bigint", SQL_BIGINT, 28);
	    break;
#endif
#ifdef WINTERFACE
#ifdef SQL_WCHAR
	case SQL_WCHAR:
	    MakeTypeInfo( 1, asize, "wchar", SQL_WCHAR, 18);
	    break;
#endif
#ifdef SQL_WVARCHAR
	case SQL_WVARCHAR:
	    MakeTypeInfo( 1, asize, "wvarchar", SQL_WVARCHAR, 19);
	    break;
#endif
#ifdef SQL_WLONGVARCHAR
	case SQL_WLONGVARCHAR:
	    MakeTypeInfo( 1, asize, "longwvarchar", SQL_WLONGVARCHAR, 20);
	    break;
#endif
#endif
	default:
	    m_nRowCount = 0;
	}
    }
	WARR()
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 결과를 만든다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::MakeResultSet(COL *colspec, int ncols, COL *colspec3,int ncols3, int *nret)
{
	STMT *s;
    DBC *d;
	//==================================================
	// 핸들을 검사 한다.
	//==================================================
    FreeResult(0);
    if (colspec3 && g_ov3) 
	{
		m_nColCount = ncols3;
		m_pDynCols = colspec3;
    } 
	else 
	{
		m_nColCount = ncols;
		m_pDynCols = colspec;
    }
	MakeBindCols(m_nColCount);
    nowchar[1] = 1;
    m_nRowCount = 0;
    rowp = -1;
    isselect = -1;
    if (nret) 
	{
		*nret = m_nColCount;
    }
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// BindCols
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::MakeBindCols(int ncols)
{
	if (m_pBindCols) 
	{
		if (m_nBindColCount < ncols) 
		{
			int i;
			BINDCOL *bindcols = (BINDCOL *)realloc(m_pBindCols, ncols * sizeof (BINDCOL));
			if (!bindcols) 
			{
				return NoMemory();
			}
			for (i = m_nBindColCount; i < ncols; i++) 
			{
				bindcols[i].type = -1;
				bindcols[i].max = 0;
				bindcols[i].lenp = NULL;
				bindcols[i].valp = NULL;
				bindcols[i].index = i;
				bindcols[i].offs = 0;
			}
			m_pBindCols = bindcols;
			m_nBindColCount = ncols;
		}
    } 
	else if (ncols > 0) 
	{
		XMALLOC(m_pBindCols,BINDCOL *,ncols * sizeof (BINDCOL))
		if (!m_pBindCols) 
		{
			return NoMemory();
		}
		m_nBindColCount = ncols;
		UnBindCols();
    }
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Bind를 해제 한다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::UnBindCols()
{
    int i;
    bkmrkcol.type = -1;
    bkmrkcol.max = 0;
    bkmrkcol.lenp = NULL;
    bkmrkcol.valp = NULL;
    bkmrkcol.index = 0;
    bkmrkcol.offs = 0;
	if( NULL == m_pBindCols)
		return;
    for (i = 0; i < m_nBindColCount; i++) 
	{
		m_pBindCols[i].type = -1;
		m_pBindCols[i].max = 0;
		m_pBindCols[i].lenp = NULL;
		m_pBindCols[i].valp = NULL;
		m_pBindCols[i].index = i;
		m_pBindCols[i].offs = 0;
    }
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Type를 만든다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::MakeTypeInfo(int row,int asize,char *ptypename, int type, int tind)
{
    int offs = row * asize;
	char *tcode, *crpar = NULL, *quote = NULL, *sign = stringify(SQL_FALSE);
    static char tcodes[32 * 32];

    if (tind <= 0) 
	{
		tind = row;
    }
    tcode = tcodes + tind * 32;
    sprintf(tcode, "%d", type);
	m_arr[offs + 0] = ptypename;
    m_arr[offs + 1] = tcode;
    if (asize >= 17) 
	{
		m_arr[offs + 15] = tcode;
		m_arr[offs + 16] = "0";
    }
    switch (type) 
	{
	default:
#ifdef SQL_LONGVARCHAR
    case SQL_LONGVARCHAR:
#ifdef WINTERFACE
    case SQL_WLONGVARCHAR:
#endif
		crpar = "length";
		quote = "'";
		sign = NULL;
		m_arr[offs + 2] = "65536";
		break;
#endif
#ifdef SQL_BIT
    case SQL_BIT:
		sign = NULL;
		m_arr[offs + 2] = "1";
		break;
#endif
    case SQL_CHAR:
    case SQL_VARCHAR:
#ifdef WINTERFACE
    case SQL_WCHAR:
    case SQL_WVARCHAR:
#endif
		m_arr[offs + 2] = "255";
		crpar = "length";
		quote = "'";
		sign = NULL;
		break;
    case SQL_TINYINT:
		m_arr[offs + 2] = "3";
		break;
    case SQL_SMALLINT:
		m_arr[offs + 2] = "5";
		break;
    case SQL_INTEGER:
		m_arr[offs + 2] = "9";
		break;
#ifdef SQL_BIGINT
    case SQL_BIGINT:
		m_arr[offs + 2] = "19";
		break;
#endif
    case SQL_FLOAT:
		m_arr[offs + 2] = "7";
		break;
    case SQL_DOUBLE:
		m_arr[offs + 2] = "15";
		break;
#ifdef SQL_TYPE_DATE
    case SQL_TYPE_DATE:
#endif
    case SQL_DATE:
		m_arr[offs + 2] = "10";
		quote = "'";
		sign = NULL;
		break;
#ifdef SQL_TYPE_TIME
    case SQL_TYPE_TIME:
#endif
    case SQL_TIME:
		m_arr[offs + 2] = "8";
		quote = "'";
		sign = NULL;
		break;
#ifdef SQL_TYPE_TIMESTAMP
    case SQL_TYPE_TIMESTAMP:
#endif
    case SQL_TIMESTAMP:
		m_arr[offs + 2] = "32";
		quote = "'";
		sign = NULL;
		break;
    case SQL_VARBINARY:
		sign = NULL;
		m_arr[offs + 2] = "255";
		break;
    case SQL_LONGVARBINARY:
		sign = NULL;
		m_arr[offs + 2] = "65536";
		break;
    }
    m_arr[offs + 3] = m_arr[offs + 4] = quote;
    m_arr[offs + 5] = crpar;
    m_arr[offs + 6] = stringify(SQL_NULLABLE);
    m_arr[offs + 7] = stringify(SQL_FALSE);
    m_arr[offs + 8] = stringify(SQL_SEARCHABLE);
    m_arr[offs + 9] = sign;
    m_arr[offs + 10] = stringify(SQL_FALSE);
    m_arr[offs + 11] = stringify(SQL_FALSE);
    m_arr[offs + 12] = ptypename;
    switch (type) 
	{
    case SQL_DATE:
    case SQL_TIME:
		m_arr[offs + 13] = "0";
		m_arr[offs + 14] = "0";
		break;
#ifdef SQL_TYPE_TIMESTAMP
    case SQL_TYPE_TIMESTAMP:
#endif
    case SQL_TIMESTAMP:
		m_arr[offs + 13] = "0";
		m_arr[offs + 14] = "3";
		break;
    default:
		m_arr[offs + 13] = NULL;
		m_arr[offs + 14] = NULL;
		break;
    }
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
//Driver's Table.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::DrvTables(
	  SQLCHAR *cat, SQLSMALLINT catLen,
	  SQLCHAR *schema, SQLSMALLINT schemaLen,
	  SQLCHAR *table, SQLSMALLINT tableLen,
	  SQLCHAR *type, SQLSMALLINT typeLen)
{
    SQLRETURN ret;
    int asize, rc, size, npatt;
    char *sql, tname[512];
    char *szWhere = "(type = 'table' or type = 'view')";

    ret = MakeResultSet(tableSpec2, array_size(tableSpec2),
		      tableSpec3, array_size(tableSpec3), &asize);
    if (ret != SQL_SUCCESS) 
	{
		return ret;
    }
    if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] == '%') 
	{
		int size = 3 * asize;
		memset(rows, 0, sizeof (char *) * size);
		m_nColCount = asize;
		m_arr[m_nColCount + 0] = "";
		m_arr[m_nColCount + 1] = "";
		m_arr[m_nColCount + 2] = "";
		m_arr[m_nColCount + 3] = "TABLE";
		m_arr[m_nColCount + 5] = "";
		m_arr[m_nColCount + 6] = "";
		m_arr[m_nColCount + 7] = "";
		m_arr[m_nColCount + 8] = "VIEW";
		nrowfreetype = FREE_TYPE_ENGFREE;
		m_nRowCount = 2;
		rowp = -1;
		return SQL_SUCCESS;
    }
    if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') 
	{
		table = NULL;
		goto doit;
    }
    if (schema && (schemaLen > 0 || schemaLen == SQL_NTS) &&
	schema[0] == '%') 
	{
	if ((!cat || catLen == 0 || !cat[0]) &&
	    (!table || tableLen == 0 || !table[0])) 
	{
	    table = NULL;
	    goto doit;
	}
    }
    if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] != '\0') 
	{
		char tmp[256], *t;
		int with_view = 0, with_table = 0;
		
		if (typeLen == SQL_NTS) 
		{
			strncpy(tmp, (char *) type, sizeof (tmp));
			tmp[sizeof (tmp) - 1] = '\0';
		} 
		else 
		{
			int len = min(sizeof (tmp) - 1, typeLen);

		    strncpy(tmp, (char *) type, len);
		    tmp[len] = '\0';
		}
		t = tmp;
		while (*t) 
		{
			*t = TOLOWER(*t);
			t++;
		}
		t = tmp;
		unescpat(t);
		while (t) 
		{
			if (t[0] == '\'') 
			{
				++t;
			}
			if (strncmp(t, "table", 5) == 0) 
			{
				with_table++;
			} 
			else if (strncmp(t, "view", 4) == 0) 
			{
				with_view++;
			}
			t = strchr(t, ',');
			if (t) 
			{
				++t;
			}
		}
		if (with_view && with_table) 
		{
			/* where is already preset */
		} 
		else if (with_view && !with_table) 
		{
			szWhere = "type = 'view'";
		} else if (!with_view && with_table) 
		{
			szWhere = "type = 'table'";
		} else 
		{
			return SQL_SUCCESS;
		}
    }
doit:
    if (!table) 
	{
		size = 1;
		tname[0] = '%';
    } 
	else 
	{
		if (tableLen == SQL_NTS) 
		{
			size = sizeof (tname) - 1;
		} 
		else 
		{
			size = min(sizeof (tname) - 1, tableLen);
		}
		strncpy(tname, (char *) table, size);
    }
    tname[size] = '\0';
    npatt = unescpat(tname);
	m_pDbc->xcelqrx = 1;
    sql = sqlite3_mprintf("select %s as 'TABLE_QUALIFIER', "
			  "%s as 'TABLE_OWNER', "
			  "tbl_name as 'TABLE_NAME', "
			  "upper(type) as 'TABLE_TYPE', "
			  "NULL as 'REMARKS' "
			  "from sqlite_master where %s "
			  "and tbl_name %s %Q",
			  m_pDbc->xcelqrx ? "''" : "NULL",
			  m_pDbc->xcelqrx ? "'main'" : "NULL",
			  szWhere,
			  npatt ? "like" : "=", tname);
    if (!sql) 
	{
		return NoMemory();
    }
	int nCols = m_nColCount;
	BOOL bSuccess = GetTable(sql);
	WARR()
    sqlite3_free(sql);
    if (TRUE == bSuccess) 
	{
		if (nCols != m_nColCount) 
		{
			FreeResult(0);
			m_nRowCount = 0;
		} 
		else 
		{
			nrowfreetype = FREE_TYPE_TABLE;
		}
    } 
	else 
	{
		m_nRowCount = 0;
		rows = NULL;
		nrowfreetype = FREE_TYPE_NULL;
	    SetState(rc, "%s", g_ov3 ? "HY000" : "S1000",m_szErr);
	    return SQL_ERROR;
    }
    rowp = -1;
	m_nArrIdx = 0;
    return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Bind Col
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::BindCol(SQLUSMALLINT col, SQLSMALLINT type,
	   SQLPOINTER val, SQLLEN max, SQLLEN *lenp)
{
	int sz = 0;
	if (col < 1) 
	{
		if (col == 0 && bkmrk && type == SQL_C_BOOKMARK) 
		{
			bkmrkcol.type = type;
			bkmrkcol.max = sizeof (SQLINTEGER);
			bkmrkcol.lenp = lenp;
			bkmrkcol.valp = val;
			bkmrkcol.offs = 0;
			if (lenp) 
			{
				*lenp = 0;
			}
			return SQL_SUCCESS;
		}
		SetState(-1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (MakeBindCols(col) != SQL_SUCCESS) 
	{
		return SQL_ERROR;
    }
    --col;
    if (type == SQL_C_DEFAULT) 
	{
		type = mapdeftype(type, m_pDynCols[col].type, 0,
				  nowchar[0] || nowchar[1]);
    }
    switch (type) 
	{
    case SQL_C_LONG:
    case SQL_C_ULONG:
    case SQL_C_SLONG:
		sz = sizeof (SQLINTEGER);
		break;
    case SQL_C_TINYINT:
    case SQL_C_UTINYINT:
    case SQL_C_STINYINT:
		sz = sizeof (SQLCHAR);
		break;
    case SQL_C_SHORT:
    case SQL_C_USHORT:
    case SQL_C_SSHORT:
		sz = sizeof (short);
		break;
    case SQL_C_FLOAT:
		sz = sizeof (SQLFLOAT);
		break;
    case SQL_C_DOUBLE:
		sz = sizeof (SQLDOUBLE);
		break;
    case SQL_C_TIMESTAMP:
		sz = sizeof (SQL_TIMESTAMP_STRUCT);
		break;
    case SQL_C_TIME:
		sz = sizeof (SQL_TIME_STRUCT);
		break;
    case SQL_C_DATE:
		sz = sizeof (SQL_DATE_STRUCT);
		break;
    case SQL_C_CHAR:
	break;
#ifdef WINTERFACE
    case SQL_C_WCHAR:
		break;
#endif
#ifdef SQL_C_TYPE_DATE
    case SQL_C_TYPE_DATE:
		sz = sizeof (SQL_DATE_STRUCT);
		break;
#endif
#ifdef SQL_C_TYPE_TIME
    case SQL_C_TYPE_TIME:
		sz = sizeof (SQL_TIME_STRUCT);
		break;
#endif
#ifdef SQL_C_TYPE_TIMESTAMP
	case SQL_C_TYPE_TIMESTAMP:
		sz = sizeof (SQL_TIMESTAMP_STRUCT);
		break;
#endif
#ifdef SQL_BIT
    case SQL_C_BIT:
		sz = sizeof (SQLCHAR);
		break;
#endif
    case SQL_C_BINARY:
		break;
#ifdef SQL_BIGINT
    case SQL_C_SBIGINT:
    case SQL_C_UBIGINT:
		sz = sizeof (SQLBIGINT);
		break;
#endif
    default:
		if (val == NULL) 
		{
			/* fall through, unbinding column */
			break;
		}
		SetState( -1, "invalid type %d", "HY003", type);
		return SQL_ERROR;
    }
    if (val == NULL) 
	{
		/* unbind column */
		m_pBindCols[col].type = -1;
		m_pBindCols[col].max = 0;
		m_pBindCols[col].lenp = NULL;
		m_pBindCols[col].valp = NULL;
		m_pBindCols[col].offs = 0;
	} 
	else 
	{
		if (sz == 0 && max < 0) 
		{
			SetState( -1, "invalid length", "HY090");
			return SQL_ERROR;
		}
		m_pBindCols[col].type = type;
		m_pBindCols[col].max = (sz == 0) ? max : sz;
		m_pBindCols[col].lenp = lenp;
		m_pBindCols[col].valp = val;
		m_pBindCols[col].offs = 0;
		if (lenp) 
		{
			*lenp = 0;
		}
    }
    return SQL_SUCCESS; 
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Do Fetch Bind.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
int CSTMT::DoFetchBind()
{
	if( NULL == m_pBindCols)
		return SQL_SUCCESS;
	for (int i = 0; i < m_nColCount; i++) 
	{
		BINDCOL *b = &m_pBindCols[i];
		if( b == NULL)
		{
			break;
		}
		if( SQL_SUCCESS != GetRowData((SQLUSMALLINT) i, b->type, b->valp, b->max, b->lenp, 0))
		{
			return SQL_ERROR;
		}
	}
	return SQL_SUCCESS;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// FreeResult();
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::FreeResult(const int nClearCols)
{
	//==================================================
	// Array 삭제.
	//==================================================
	ArrDeleteAll();
	//==================================================
	// Bind 삭제.
	//==================================================
	XFREE(bincache);
    bincell = NULL;
    binlen = 0;
    m_nRowCount = -1;
    if (nClearCols > 0) 
	{
		XFREE(m_pBindCols);
		m_nBindColCount = 0;
    }
    if (nClearCols) 
	{
		FreeDynCols();
		m_nColCount = 0;
		nowchar[1] = 0;
    }
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Array Operaion.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Memory를 생성하고 넣는다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::ArrAddAlloc(const int nIndex, const char * pszVal)
{
	char *pBuf = m_arr[nIndex];
	if( pBuf != NULL)
	{
		free(pBuf);
	}
	XMALLOC(pBuf,char*,strlen(pszVal) + 1);
	memcpy(pBuf,pszVal,strlen(pszVal));
	m_arr[nIndex] = pBuf;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 메모리를 생성하지 않고 그냥 넣는다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::ArrAdd(const int nIndex, const char * pszVal)
{
	char *pBuf = m_arr[nIndex];
	if( pBuf != NULL)
	{
		free(pBuf);
	}
	m_arr[nIndex] = pBuf;
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// 모든 데이터를 지운다.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::ArrDeleteAll()
{
	POSITION  pos = m_arr.GetStartPosition();
	int nKey;
	char * pVal;
	while ( pos != NULL )
	{
		m_arr.GetNextAssoc(pos, nKey, pVal);
		if( NULL != (pVal = m_arr[nKey]))
		{
			free(pVal);
		}
		m_arr.RemoveKey(nKey);
	}
	m_arr.RemoveAll();
}
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
// Write array to file. - Debug를 위한 것.
//▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
void CSTMT::WriteArray(const char * pFile,const char * pFunc, const int nLine)
{
	POSITION  pos = m_arr.GetStartPosition();
	int nKey;
	char * pVal;
	//==========================================
	char szCount[50];
	memset(szCount,0,50);
	sprintf_s(szCount,50,"m_nColCount:%d,m_nRowCount:%d",m_nColCount,m_nRowCount);
	util_AppendLogs( pFile,pFunc, nLine, "=========================");
	util_AppendLogs( pFile,pFunc, nLine, szCount);
	util_AppendLogs( pFile,pFunc, nLine, "=========================");
	//==========================================
	while ( pos != NULL )
	{
		m_arr.GetNextAssoc(pos, nKey, pVal);
		if( NULL == (pVal = m_arr[nKey]))
		{
			util_AppendLogs( pFile,pFunc, nLine, "NULL");
		}
		else
		{
			util_AppendLogs( pFile,pFunc, nLine, pVal);
		}
	}
}
